package com.epoch.base.seq;

import com.epoch.base.constant.Constant;
import com.epoch.base.constant.DictCodeConstant;
import com.epoch.base.seq.dao.SeriaNumber;
import com.epoch.base.util.DateUtils;
import com.jfinal.plugin.activerecord.Db;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

public class SerialNumberGeneration {
    /**
     * 日志记录
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(SerialNumberGeneration.class);


    public static String anewUpdateAndGetSerialNum(String dateRegex, String ruleCode, String inSplit, String inPrefix) {
        if (StringUtils.isBlank(ruleCode)) {
            LOGGER.error("-##流水号规则编码不能为空。");
            return null;
        }
        //根据流水规则编码更新当前流水
        String sql = "update sys_serial_number n set n.current_value = n.current_value + n.step where n.rule_code = '" + ruleCode + "'";
        Db.update(sql);
        //根据规则编码获取当前流水号及规则信息
        sql = "select n.*,SYSDATE() as system_time from sys_serial_number n where n.rule_code = '" + ruleCode + "'";
        SeriaNumber seriaNumber = SeriaNumber.dao.findFirst(sql);
        if (seriaNumber != null) {
            // 流水号重置规则
            String ruleType = seriaNumber.getStr(SeriaNumber.RULETYPE);
            // 数据库中当前时间
            Date curtime = seriaNumber.getDate(SeriaNumber.CURTIME);
            // 流水号长度
            int serialLength = seriaNumber.getInt(SeriaNumber.SERIALLENGTH);
            // 当前值
            String currentValue = seriaNumber.getStr(SeriaNumber.CURRENTVALUE);
            // 初始值
            String initValue = seriaNumber.getStr(SeriaNumber.INITVALUE);
            // 系统当前时间（放在更新操作后面，防止时间判断误差）
            Date systemTime = seriaNumber.getDate(SeriaNumber.SYSTEMTIME);
            // 初始值
            String prefix = seriaNumber.getStr(SeriaNumber.PREFIX);
            // 初始值
            String split = seriaNumber.getStr(SeriaNumber.SPLIT);
            // 流水号
            String serialNum;

            // 判断是否需要重置流水号
            if (checkIsReset(ruleType, DateUtils.getDateToString(curtime, DateUtils.DATEFORMAT_YYYYMMDDHHMMSS), DateUtils.getDateToString(systemTime, DateUtils.DATEFORMAT_YYYYMMDDHHMMSS))) {
                serialNum = currentValue;
            } else {
                // 取初始值（重置）
                serialNum = initValue;
                seriaNumber.set(SeriaNumber.CURTIME, systemTime);
                seriaNumber.set(SeriaNumber.CURRENTVALUE, initValue);
                seriaNumber.update();
            }
            // 根据流水号长度返回组装后的流水号
            if (null != serialNum) {

                String middle = StringUtils.isNotBlank(dateRegex) ? DateUtils.dateTimeFormat(systemTime,
                        DateUtils.DATEFORMAT_YYYYMMDDHHMMSS, dateRegex) : Constant.STR_EMPTY;
                StringBuilder sb = new StringBuilder();
                if (StringUtils.isNotBlank(inPrefix)) {
                    sb.append(inPrefix);
                } else if (StringUtils.isNotBlank(prefix)) {
                    sb.append(prefix);
                }
                if (StringUtils.isNotBlank(inSplit)) {
                    sb.append(inSplit);
                } else if (StringUtils.isNotBlank(split)) {
                    sb.append(split);
                }
                if (StringUtils.isNotBlank(middle)) {
                    sb.append(middle);
                }
                if (StringUtils.isNotBlank(inSplit)) {
                    sb.append(inSplit);
                } else if (StringUtils.isNotBlank(split)) {
                    sb.append(split);
                }
                String after = assembleSerialNum(serialLength, serialNum);
                if (StringUtils.isNotBlank(after)) {
                    sb.append(after);
                }
                return sb.toString();
            }
        }
        LOGGER.error("-##根据流水号规则编码：{} 没有找到对应的规则。", ruleCode);
        return null;
    }

    /**
     * 功能描述: <br>
     * 〈根据流水号规则判断是否需要重置流水号〉
     *
     * @param ruleType   规则编号
     * @param curtime    数据库中当前时间
     * @param systemTime 系统当前时间
     * @return true：不需要重置，false：需要重置
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static boolean checkIsReset(String ruleType, String curtime, String systemTime) {
        boolean flag;
        switch (ruleType) {
            case DictCodeConstant.SYS_SERIAL_NUMBER_DAY:// 按日重置（使用率最高，放在最前）
                flag = curtime.substring(Constant.INT0, Constant.INT8).equals(
                        systemTime.substring(Constant.INT0, Constant.INT8));
                break;
            case DictCodeConstant.SYS_SERIAL_NUMBER_NEVER:// 永远都不重置
                flag = true;
                break;
            case DictCodeConstant.SYS_SERIAL_NUMBER_YEAR:// 按年重置
                flag = curtime.substring(Constant.INT0, Constant.INT4).equals(
                        systemTime.substring(Constant.INT0, Constant.INT4));
                break;
            case DictCodeConstant.SYS_SERIAL_NUMBER_MONTH:// 按月重置
                flag = curtime.substring(Constant.INT0, Constant.INT6).equals(
                        systemTime.substring(Constant.INT0, Constant.INT6));
                break;
            case DictCodeConstant.SYS_SERIAL_NUMBER_HOUR:// 按小时重置
                flag = curtime.substring(Constant.INT0, Constant.INT10).equals(
                        systemTime.substring(Constant.INT0, Constant.INT10));
                break;
            case DictCodeConstant.SYS_SERIAL_NUMBER_MINUTE:// 按分重置
                flag = curtime.substring(Constant.INT0, Constant.INT12).equals(
                        systemTime.substring(Constant.INT0, Constant.INT12));
                break;
            case DictCodeConstant.SYS_SERIAL_NUMBER_SECOND:// 按秒重置
                flag = curtime.equals(systemTime);
                break;
            default:// 永远都不重置
                flag = true;
                break;
        }
        return flag;
    }

    /**
     * 功能描述: <br>
     * 〈根据流水号长度拼接流水号〉
     *
     * @param serialLength 流水号设置的长度
     * @param serialNum    获取递增后的流水号
     * @return 拼接后的流水号
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static String assembleSerialNum(int serialLength, String serialNum) {
        // 初始化拼接后的流水号
        StringBuilder num = new StringBuilder();
        // 判断设置长度是否大于当前流水号
        if (serialLength > serialNum.length()) {
            // 循环在当前流水号前补0
            for (int i = 0; i < serialLength - serialNum.length(); i++) {
                num.append(Constant.STR0);
            }
            return num.append(serialNum).toString();
        }
        // 如果长度不大于当前流水号则直接返回当前流水号
        return serialNum;
    }
}
